/**
 * Copyright (C) 2006 - present David Bulmore
 * All Rights Reserved.
 *
 * This file is part of Easy Java Websites.
 *
 * EJW is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the accompanying license 
 * for more details.
 *
 * You should have received a copy of the license along with EJW; if not, 
 * go to http://www.EasierJava.com and download the latest version.
 */

package ejw;

import ejw.utilities.ObjectFiller;
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ejw.utilities.ObjectFiller.GetHandler;
import java.io.Serializable;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * Provides access to the ejw/servlet environment.
 */
@SuppressWarnings("unchecked")
public class ServerInterface
  {
    public static final int UNDEFINED_ERROR = 0;
    public static final int VALIDATION_ERROR = 1;
    public static final int NOT_SECURE_ERROR = 2;
    public static final int AUTHENTICATION_ERROR = 3;

    private static Logger logger = LoggerFactory.getLogger(ServerInterface.class);
  
    private HttpServletRequest servletRequest;
    private HttpServletResponse servletResponse;
    private Map multipartFormDataMap, cookieMap;
    private ServletConfig servletConfig;
    private DataManager dataManager;
    private DataManager.RequestData requestData;
    private UrlDataWrapper urlDataWrapper = new UrlDataWrapper();
    private String returnedUrlKey, useDomainName;
    private boolean urlRewriting;
    
    public ServerInterface(HttpServletRequest servletRequest, HttpServletResponse servletResponse, ServletConfig servletConfig)
      {
        this.servletRequest = servletRequest;
        this.servletResponse = servletResponse;
        this.servletConfig = servletConfig;
        this.dataManager = new DataManager();

        initFormData();
      }

    protected ServerInterface(HttpServletRequest servletRequest, HttpServletResponse servletResponse, ServletConfig servletConfig, DataManager dataManager, String useDomainName, boolean urlRewriting)
      {
        this.servletRequest = servletRequest;
        this.servletResponse = servletResponse;
        this.servletConfig = servletConfig;
        this.dataManager = dataManager;
        this.useDomainName = useDomainName;
        this.urlRewriting = urlRewriting;

        initFormData();
      }

    /**
     * Returns the logger.
     */
    public static Logger getLogger() { return logger; }
    
    /**
     * Returns a list of the current users request history.  The list contains
     * string objects representing the request URIs (plus their query strings) that 
     * the user has visited during this session.  This information 
     * can be used in conjunction with ServerInterface.forwardRequest() to forward the user to the last/previous 
     * relevant request after signing on.
     *
     * @return returns a list of string objects
     */
    public List getRequestHistory() { return (List)getSessionAttribute("ejwRequestHistory"); }
    
    /**
     * Add URI to the request history (stored in the users session).
     */
    
    public void addToRequestHistory()
      {
        addToRequestHistory(20);
      }
    
    /**
     * Add URI to the request history (stored in the users session).
     *
     * @param limit the maximum number of URIs in the history list
     */
    
    public void addToRequestHistory(int limit)
      {
        addToRequestHistory(limit, servletRequest.getRequestURI() + (servletRequest.getQueryString() == null ? "" : "?" + servletRequest.getQueryString()));
      }

    /**
     * Add URI to the request history (stored in the users session).
     *
     * @param limit the maximum number of URIs in the history list
     * @param uri the URI being stored
     */
    
    public void addToRequestHistory(int limit, String uri)
      {
        List requestHistory = (List)getSessionAttribute("ejwRequestHistory");
        
        if (requestHistory == null)
          setSessionAttribute("ejwRequestHistory",requestHistory = new ArrayList());
        
        requestHistory.add(uri);
        
        if (requestHistory.size() > limit)
          requestHistory.remove(0);
      }

    /**
     * Forward request to another URL within the same web application context.  The calling RequestHandler method
     * must return "noForwarding", as forwarding/redirects can not be called twice.  This method calls 
     * ServletRequest.getRequestDispatcher().
     *
     * @param forwardUrl the URL to forward to
     */
    public void forwardRequest(String forwardUrl) throws WebAppException
      {
        logger.debug("forward URL={}", forwardUrl);
        
        RequestDispatcher requestDispatcher = getServletRequest().getRequestDispatcher(forwardUrl);

        try
          {
            if (requestDispatcher != null)
              requestDispatcher.forward(getServletRequest(),getServletResponse());
          }
        catch (Exception e)
          {
            throw new WebAppException(e);
          }
      }

    /**
     * Redirect the request to another URL.  This URL can be anywhere inside or outside the web application context.
     * The calling RequestHandler method must return "noForwarding", as forwarding/redirects can not be called twice.  This 
     * method calls ServletResponse.sendRedirect().
     *
     * @param redirectUrl the URL to forward to
     */
    public void redirectRequest(String redirectUrl) throws WebAppException
      {
        try
          {
            getServletResponse().sendRedirect(redirectUrl);
          }
        catch (Exception e)
          {
            throw new WebAppException(e);
          }
      }
    
    /**
     * Validates the request data with XML defined validations.
     *
     * @return true if the validation passes, false if it does not pass.
     */
    public boolean validateParameters()
      {
        return ValidationManager.validateParameters(this);
      }
    
    DataManager getDataManager() { return dataManager; }
    
    /**
     * Return a request parameter defined in the configuration webapp.xml file.
     * 
     * @return a string value
     */
    public String getRequestParameter(String parameterName) { return requestData.getParameter(parameterName); }

    /**
     * Return a set of request parameter names defined in the configuration webapp.xml file.
     *
     * @return a set of string values
     */
    public Set<String> getRequestParameterNames() { return requestData.getParameterMap().keySet(); }

    DataManager.RequestData getRequestData() { return this.requestData; }
    void setRequestData(DataManager.RequestData requestData) { this.requestData = requestData; }
    
    /**
     * Returns the last URL data key returned by a method from RequestHandler.
     * @return last URL data key string
     */
    public String getReturnedUrlKey() { return returnedUrlKey; }
    
    void setReturnedUrlKey(String urlDataKey) { this.returnedUrlKey = urlDataKey; }
    
    /**
     * Loads your object with request parameter data.  "obj" is an object of any type 
     * containing set methods.  Any set method matching the name of a parameter will
     * be set with the request parameter value.
     *
     * @param obj an object with set methods
     *
     * @return object with set methods
     */
    public <T> T fillObject(T obj) throws WebAppException
      {
        return fillObject(obj, true, false, null, false);
      }
    
    /**
     * Loads your object with request parameter data.  "obj" is an object of any type 
     * containing set methods.  Any set method matching the name of a parameter will
     * be set with the request parameter value.
     *
     * @param obj an object with set methods
     * @param setNulls set or ignore null values
     *
     * @return object with set methods
     */
    public <T> T fillObject(T obj, boolean setNulls) throws WebAppException
      {
        return fillObject(obj, true, false, null, setNulls);
      }
    
    /**
     * Loads your object with request parameter data.  "obj" is an object of any type
     * containing set methods.  Any set method matching the name of a parameter will
     * be set with the request parameter value.
     *
     * @param obj an object with set methods
     * @param ignoreValueNames ignore the value names in the set
     * @param setNulls set or ignore null values
     *
     * @return object with set methods
     */
    public <T> T fillObject(T obj, Set<String> ignoreValueNames, boolean setNulls) throws WebAppException
      {
        return fillObject(obj, true, false, ignoreValueNames, setNulls);
      }

    /**
     * Loads your object with request parameter data.  "obj" is an object of any type 
     * containing set methods.  Any set method matching the name of a parameter will
     * be set with the request parameter value.
     *
     * @param obj an object with set methods
     * @param fillPublicSetMethods fill the public set methods
     * @param fillPublicFields fill the public fields
     * @param ignoreValueNames ignore the value names in the set
     * @param setNulls set or ignore null values
     *
     * @return object with set methods
     */
    public <T> T fillObject(T obj, boolean fillPublicSetMethods, boolean fillPublicFields, Set<String> ignoreValueNames, boolean setNulls) throws WebAppException
      {
        try
          {
            ObjectFiller.fillObject(new GetHandler()
              {
                public Object get(String key, Class objectClass) 
                  {
                    String[] arr = getParameters(key);
                    
                    if (arr != null && !objectClass.isArray())
                      return arr[0];
                       
                    return arr;
                  }
              }, obj, fillPublicSetMethods, fillPublicFields, ignoreValueNames, setNulls);

            return obj;
          }
        catch (Exception e)
          {
            throw new WebAppException(e);
          }
      }

    /**
     * Return the servlets config.
     * @return an instance of ServletConfig
     */
    public ServletConfig getServletConfig() { return servletConfig; }

    /**
     * Return the servlets context.
     * @return an instance of ServletContext
     */
    public ServletContext getServletContext() { return servletConfig.getServletContext(); }

    /**
     * Return the servlet request.
     * @return an instance of HttpServletRequest
     */
    public HttpServletRequest getServletRequest() { return servletRequest; }
    
    /**
     * Return the servlet response.
     * @return an instance of HttpServletResponse
     */
    public HttpServletResponse getServletResponse() { return servletResponse; }

    /**
     * Returns initialization parameters defined in web.xml.  First checks servlet context, then servlet config.
     * 
     * @param parameterName the name of the parameter
     * 
     * @return the initialization parameter
     */
    public String getInitParameter(String parameterName) { return getInitParameter(parameterName, null); }

    /**
     * Returns initialization parameters defined in web.xml.  First checks servlet config, then servlet context.
     * 
     * @param parameterName the name of the parameter
     * @param defaultValue if no initialization parameters exist use the default value
     * 
     * @return the initialization parameter
     */
    public String getInitParameter(String parameterName, String defaultValue)
      {
        String parameter = servletConfig.getInitParameter(parameterName);
        
        if (parameter == null)
          parameter = servletConfig.getServletContext().getInitParameter(parameterName);
        
        if (parameter == null)
          return defaultValue;
        else
          return parameter;
      }
    
    /**
     * Returns the cookie associated with key.
     * 
     * @param key the name of the cookie
     * @return the cookie associated with key.
     */
    public Cookie getCookie(String key)
      {
        if (cookieMap == null)
          {
            cookieMap = new HashMap();
            
            Cookie[] cookies = getServletRequest().getCookies();
            
            if (cookies != null)
              for (int i = 0; i < cookies.length; i++)
                cookieMap.put(cookies[i].getName(), cookies[i]);
          }

        return (Cookie)cookieMap.get(key);
      }
    
    /**
     * Log a message via ServletContext.log().
     *
     * @param message to log
     */
    public void log(String message)
      {
        getServletContext().log(message);
      }
    
    /**
     * Log a message via ServletContext.log().
     *
     * @param message to log
     * @param t instance to log
     */
    public void log(String message, Throwable t)
      {
        getServletContext().log(message,t);
      }
    
    /**
     * Returns the HTTP (GET, POST, PUT, DELETE, HEAD, OPTIONS) method used to make the request.
     * 
     * @return a String (GET, POST, PUT, DELETE, HEAD, OPTIONS)
     */
    public String getHttpMethod() { return getServletRequest().getMethod(); }
    
    /**
     * Returns the extra path information parsed by '/' as a String[].
     * 
     * @return the extra path information parsed by '/' as a String[].
     */
    public String[] getPathArguments()
      {
        String pathInfo = getServletRequest().getPathInfo();
        
        if (pathInfo == null)
          return new String[0];

        if (pathInfo.startsWith("/"))
          pathInfo = pathInfo.substring(1);

        return pathInfo.split("/");
      }
    
    /**
     * Checks if the current user has permission.
     * @param roles the the current user belongs to
     * @return true if the user has permission, false otherwise
     */
    public boolean hasPermission(Set<String> roles)
      {
        if (roles == null || roles.isEmpty())
          return true;
        
        if (getRemoteUserInformation() == null && getRemoteUser() == null)
          return false;

        Iterator<String> it = roles.iterator();

        while (it.hasNext())
          {
            if (isUserInRole(it.next()))
              return true;
          }
        
        return false;
      }
    
    /**
     * Returns the remote user Id.
     * @return the remote user Id
     */
    public String getRemoteUser() 
      {
        String remoteUser = servletRequest.getRemoteUser();

        if (remoteUser != null)
          return remoteUser;

        return (String)getSessionAttribute("ejwRemoteUser");
      }

    /**
     * Set the remote users information.
     * @param userId the remote users Id
     * @param roles the remote user belongs to
     */
    public void setRemoteUserInformation(String userId, Map<String, ? extends Object> roles)
      {
        setRemoteUserInformation(userId, null, null, null, roles);
      }
    
    /**
     * Set the remote users information.
     * @param userId the remote users Id
     * @param salutation the remote users salutation (Mr., Miss., Mrs., ...), may be null
     * @param firstName the remote users first name, may be null
     * @param lastName the remote users last name, may be null
     * @param roles the remote user belongs to
     */
    public void setRemoteUserInformation(String userId, String salutation, String firstName, String lastName, Map<String, ? extends Object> roles)
      {
        RemoteUser.setRemoteUser(servletRequest, new RemoteUser(userId, salutation, firstName, lastName, roles));
      }
    
    /**
     * Remove remote users information from the environment (logout).
     */
    public void invalidateRemoteUser()
      {
        servletRequest.getSession().invalidate();
      }
    
    /**
     * Returns an instance of RemoteUser, which contains information (user id, first name, last name, roles) about the remote user.
     * @return an instance of RemoteUser
     */
    public RemoteUser getRemoteUserInformation()
      {
        return (RemoteUser)getSessionAttribute("ejwRemoteUserInformation");
      }
    
    /**
     * Checks if the current user is in the given role.
     *
     * @param role checks if the user is in the role
     * @return true if the user is in the role, false otherwise
     */
    public boolean isUserInRole(String role) 
      {
        RemoteUser remoteUser = getRemoteUserInformation();

        if (remoteUser != null && remoteUser.getRoleMap() != null)
          return remoteUser.getRoleMap().containsKey(role);

        return servletRequest.isUserInRole(role);
      }
    
    /**
     * Returns the request parameter associated with the given name.
     * @param parameter the key associated with parameter
     * @return the parameter
     */
    public String getParameter(String parameter)
      {
        return getParameter(parameter, null, false);
      }
    
    /**
     * Returns the request parameter associated with the given name.
     *
     * @param parameter the key associated with parameter
     * @param defaultValue return the default value if the param is empty or null
     *
     * @return the parameter
     */
    public String getParameter(String parameter, String defaultValue)
      {
        return getParameter(parameter, defaultValue, true);
      }
    
    /**
     * Returns the request parameter associated with the given name.
     * @param parameter the key associated with parameter
     * @param emptyToNull return null instead of an empty value
     * @return the parameter
     */
    public String getParameter(String parameter, boolean emptyToNull)
      {
        return getParameter(parameter, null, emptyToNull);
      }
    
    /**
     * Returns the request parameter associated with the given name.
     * @param parameter the key associated with parameter
     * @param defaultValue return the default value if the param is empty or null
     * @param emptyToNull return null instead of an empty value
     * @return the parameter
     */
    public String getParameter(String parameter, String defaultValue, boolean emptyToNull)
      {
        String value = null;

        if (isMultipartFormData())
          value = getMultipartFormParameter(parameter);
        
        if (value == null)
          value = servletRequest.getParameter(parameter);
        
        if (value == null || (value.length() == 0 && emptyToNull))
          if (defaultValue != null)
            return defaultValue;
          else
            return null;
        
        return value;
      }
    
    /**
     * Returns the request parameter names.
     * @return an list of the parameter names
     */
    public List<String> getParameterNames()
      {
        List<String> nameList = new ArrayList<String>();
        
        if (isMultipartFormData())
          {
            if (multipartFormDataMap != null)
              nameList.addAll(multipartFormDataMap.keySet());
          }
        
        Enumeration<String> e = servletRequest.getParameterNames();
        
        while (e.hasMoreElements())
          nameList.add(e.nextElement());
        
        return nameList;
      }
    
    /**
     * Returns the request parameter values associated with the given name.
     * @param parameter the key associated with parameter
     * @return the parameter values
     */
    public String[] getParameters(String parameter)
      {
        return getParameters(parameter, null, true);
      }
    
    /**
     * Returns the request parameter values associated with the given name.
     * @param parameter the key associated with parameter
     * @param defaultValue return the default value if the param is empty or null
     * @return the parameter values
     */
    public String[] getParameters(String parameter, String defaultValue)
      {
        return getParameters(parameter, defaultValue, true);
      }
    
    /**
     * Returns the request parameter values associated with the given name.
     * @param parameter the key associated with parameter
     * @param emptyToNull return null instead of an empty array
     * @return the parameter values
     */
    public String[] getParameters(String parameter, boolean emptyToNull)
      {
        return getParameters(parameter, null, emptyToNull);
      }
    
    /**
     * Returns the request parameter values associated with the given name.
     * @param parameter the key associated with parameter
     * @param defaultValue return the default value if the param is empty or null
     * @param emptyToNull return null instead of an empty array
     * @return the parameter values
     */
    public String[] getParameters(String parameter, String defaultValue, boolean emptyToNull)
      {
        String[] values = null;
        
        if (isMultipartFormData())
          values = new String[] { getParameter(parameter) };
        else if (servletRequest != null)
          values = servletRequest.getParameterValues(parameter);
        
        if (values == null || ((values.length == 0 || (values.length == 1 && (values[0] == null || values[0].length() == 0))) && emptyToNull))
          if (defaultValue != null)
            return new String[] { defaultValue };
          else
            return null;
        
        return values;
      }
    
    /**
     * Returns the request attribute associated with the given name.
     * @param objectName the key associated with the attribute
     * @return the attribute
     */
    public <T> T getViewObject(String objectName)
      {
        return (T)getAttribute(objectName);
      }

    /**
     * Anything added with addViewObject() is available to page authors, and is the best way to pass objects on to the view.
     * @param objectName the attribute name/key
     * @param object is the Object to be stored in the request for later access during request processing
     */
    public void addViewObject(String objectName, Object object)
      {
        setAttribute(objectName, object);
      }


    /**
     * Add/map an object for automatic form management.  Objects will automatically be filled in with
     * incoming request data (from form submittal).  Objects can be accessed in JSP via ${form.propertyName},
     * Where property name is a property of an added object.  For example:
     *
     * serverInterface.addFormObject("customer",new Customer);
     *
     * In JSP:
     *
     * ${form.customerName}
     *
     * @param objectName name of object being added
     * @param object object being added
     * @deprecated Use {@link #fillObject(T Object) fillObject} 
     *      and {@link #addViewObject(String objectName, T Object) addViewObject} instead
     */
    @Deprecated public <T> void addFormObject(String objectName, T object)
      {
        addFormObject(objectName, object, true);
      }

    /**
     * Add/map an object for automatic form management.  Objects will automatically be filled in with
     * incoming request data (from form submittal).  Objects can be accessed in JSP via ${form.propertyName}.
     * Where property name is a property of an added object.  For example:
     *
     * serverInterface.addFormObject("customer",new Customer);
     *
     * In JSP:
     *
     * ${form.customerName}
     *
     * @param objectName name of object being added
     * @param object object being added
     * @param overwrite overwrite previously added object
     * @deprecated Use {@link #fillObject(T Object) fillObject}
     *      and {@link #addViewObject(String objectName, T Object) addViewObject} instead
     */
    @Deprecated public <T> void addFormObject(String objectName, T object, boolean overwrite)
      {
        FormData formData = getSessionAttribute("ejwForm");

        if (formData == null)
          setSessionAttribute("ejwForm", formData = new FormData());

        if (overwrite || formData.getFormObject(objectName) == null)
          formData.put(objectName, object);
      }

    /**
     * Remove previously added form object.
     *
     * @param objectName name of object previously added
     * @deprecated Use {@link #fillObject(T Object) fillObject}
     *      and {@link #addViewObject(String objectName, T Object) addViewObject} instead
     */
    @Deprecated public void removeFormObject(String objectName)
      {
        FormData formData = getSessionAttribute("ejwForm");

        if (formData != null)
          formData.remove(objectName);
      }

    /**
     * Return previously added form object.
     *
     * @param objectName name of object previously added
     * @return previously added object
     * @deprecated Use {@link #fillObject(T Object) fillObject}
     *      and {@link #addViewObject(String objectName, T Object) addViewObject} instead
     */
    @Deprecated public <T> T getFormObject(String objectName)
      {
        FormData formData = getSessionAttribute("ejwForm");

        if (formData != null)
          return (T)formData.getFormObject(objectName);

        return null;
      }

    void initFormData()
      {
        FormData formData = getSessionAttribute("ejwForm");

        if (formData != null)
          {
            String formName = getParameter("ejwFormName", true);

            if (formName != null)
              {
                Object object = formData.getFormObject(formName);

                if (object != null)
                  try { fillObject(object, null, true); }
                  catch (WebAppException ex) { }
              }
          }
      }

    static class FormData extends HashMap implements Serializable
      {
        public Object getFormObject(String key) { return super.get(key); }

        public Object get(Object key)
          {
            String keyStr = (String)key;
            Object value = null;

            if (value == null)
              for (Iterator it = values().iterator(); value == null && it.hasNext();)
                {
                  Object obj = it.next();

                  try
                    {
                      value = obj.getClass().getDeclaredMethod("get" + Character.toUpperCase(keyStr.charAt(0)) + keyStr.substring(1)).invoke(obj);
                    }
                  catch (Exception ex) {}
                }

            return value;
          }
      }

    /**
     * Returns the request attribute associated with the given name.
     * @param objectName the key associated with the attribute
     * @return the attribute
     */
    public <T> T getAttribute(String objectName)
      {
        if (servletRequest != null)
          return (T)servletRequest.getAttribute(objectName);

        return null;
      }
    
    /**
     * Returns the request attribute associated with the given name.
     * @param objectName the key associated with the attribute
     * @param defaultValue if the attribute does not exist, return the default value.
     * @return the attribute
     */
    public <T> T getAttribute(String objectName, T defaultValue)
      {
        T obj = null;
        
        if (servletRequest != null)
          obj = (T)servletRequest.getAttribute(objectName);

        if (obj != null)
          return obj;
        
        servletRequest.setAttribute(objectName, defaultValue);
        
        return defaultValue;
      }
    
    /**
     * Anything set with setAttribute() is available to page authors.
     * @param objectName the attribute name/key
     * @param object is the Object to be stored in the request for later access during request processing
     */
    public void setAttribute(String objectName, Object object)
      {
        if (servletRequest != null)
          servletRequest.setAttribute(objectName,object);
      }

    /**
     * Removes an attribute from the request.
     * @param objectName the attribute name/key
     */
    public void removeAttribute(String objectName)
      {
        if (servletRequest != null)
          servletRequest.removeAttribute(objectName);
      }

    /**
     * Returns a session attribute.  This is the way to keep user oriented information between requests.
     * @param objectName the key associated with the stored object.
     * @return the stored object
     */
    public <T> T getSessionAttribute(String objectName)
      {
        if (servletRequest != null)
          return (T)servletRequest.getSession().getAttribute(objectName);

        return null;
      }
    
    /**
     * Returns a session object.  This is the way to keep user oriented information between requests.
     * @param objectName the key associated with the stored object.
     * @return the stored object
     */
    public <T> T getSessionObject(String objectName)
      {
        return getSessionAttribute(objectName);
      }
    
    /**
     * Returns a session attribute.  This is the way to keep user oriented information between requests.
     * @param objectName the key associated with the stored object.
     * @param defaultValue if the attribute does not exist, return the default value.
     * @return the stored object
     */
    public <T> T getSessionAttribute(String objectName, T defaultValue)
      {
        T obj = null;
          
        if (servletRequest != null)
          obj = (T)servletRequest.getSession().getAttribute(objectName);

        if (obj != null)
          return obj;
        
        servletRequest.getSession().setAttribute(objectName, defaultValue);
        
        return defaultValue;
      }
    
    /**
     * Sets a session attribute.  This is the way to keep user oriented information between requests.
     * @param objectName the key associated with the stored object.
     * @param object the object to store
     */
    public void setSessionAttribute(String objectName, Object object)
      {
        if (servletRequest != null)
          servletRequest.getSession().setAttribute(objectName,object);
      }
    
    /**
     * Adds an object to the session.  This is the way to keep user oriented information between requests.
     * @param objectName the key associated with the stored object.
     * @param object the object to store
     */
    public void addSessionObject(String objectName, Object object)
      {
        setSessionAttribute(objectName, object);
      }
    
    /**
     * Removes a session attribute.
     * @param objectName the key associated with the stored object.
     */
    public void removeSessionAttribute(String objectName)
      {
        if (servletRequest != null)
          servletRequest.getSession().removeAttribute(objectName);
      }
    
    /**
     * Returns the last exception set with setException().
     * @return the last exception set with setException().
     */
    public Exception getException() { return (Exception)getAttribute("ejwException"); }
    
    /**
     * Sets an exception and makes it available to the view via "exception".
     *
     * @param exception any valid instance of Exception or a subclass.
     */
    public void setException(Exception exception) 
      {
        setAttribute("ejwException", exception);
      }
    
    /**
     * Returns the set application message.
     * @return the application message
     */
    public String getApplicationMessage()
      {
        String str = (String)getAttribute("ejwApplicationMessage");

        return str != null ? str : "";
      }
    
    /**
     * Set the application message.  This is the method for passing information back to the user.
     * @param applicationMessage the application message
     */
    public void setApplicationMessage(String applicationMessage)
      {
        setAttribute("ejwApplicationMessage", applicationMessage);
      }

    /**
     * Returns the list of application messages.
     * @return the application messages
     */
    public List<String> getApplicationMessages()
      {
        List<String> list = (List<String>)getAttribute("ejwApplicationMessages");

        if (list == null)
          {
            list = new ArrayList<String>();
            setAttribute("ejwApplicationMessages", list);
          }
        
        return list;
      }

    /**
     * Add a message to the application messages.  This is the method for passing information back to the user.
     * @param applicationMessage the message
     */
    public void addToApplicationMessages(String applicationMessage)
      {
        getApplicationMessages().add(applicationMessage);
      }

    String resolveReferences(UrlData urlData) throws WebAppException
      {
        if (urlData != null)
          {
            String url = urlData.getUrl();

            while (url.indexOf("${") != -1)
              {
                if (url.indexOf('}') == -1)
                  throw new WebAppException("URL syntax error: URL id=" + urlData.getId() + ", url=" + url);
                
                String reference = url.substring(url.indexOf("${") + 2, url.indexOf("}")),
                       resolvedReference;
                
                if (reference.equalsIgnoreCase("contextPath"))
                  resolvedReference = servletRequest.getContextPath();
                else if (reference.equalsIgnoreCase("pathInfo"))
                  resolvedReference = servletRequest.getPathInfo();
                else if (reference.equalsIgnoreCase("queryString"))
                  resolvedReference = servletRequest.getQueryString();
                else if (reference.equalsIgnoreCase("servletPath"))
                  resolvedReference = servletRequest.getServletPath();
                else if (reference.equalsIgnoreCase("scheme"))
                  resolvedReference = servletRequest.getScheme();
                else if (reference.equalsIgnoreCase("requestURI"))
                  resolvedReference = servletRequest.getRequestURI();
                else if (reference.equalsIgnoreCase("serverName"))
                  resolvedReference = servletRequest.getServerName();
                else if (reference.equalsIgnoreCase("serverPort"))
                  resolvedReference = "" + servletRequest.getServerPort();
                else
                  resolvedReference = resolveReferences(dataManager.getUrlData(reference));
                
                if (resolvedReference == null)
                  resolvedReference = "";
                
                url = url.substring(0, url.indexOf("${")) + resolvedReference + url.substring(url.indexOf("}") + 1);
              }
            
            return url;
          }
        
        return null;
      }

    /**
     * Takes a context relative path/uri and returns the absolute path/url (complete url).
     *
     * @param contextRelativeUri the context relative uri
     * @param includePort include :port
     * @return the absolute url
     */
    public String getAbsoluteUrl(String contextRelativeUri) throws WebAppException
      {
        return getBuiltUrl(new UrlData("absoluteUrl", contextRelativeUri), true, urlRewriting);
      }
    
    String getBuiltUrl(UrlData urlData, boolean fullUrl, boolean rewrite) throws WebAppException
      {
        String requestProtocol = getServletRequest().getScheme(),
               protocol = urlData.getProtocol() != null ? urlData.getProtocol() : requestProtocol;
        String forwardUrl = resolveReferences(urlData), returnUrl = "";
        
        try
          {
            URI uri = new URI(forwardUrl);

            if (fullUrl && forwardUrl != null && protocol != null && uri.getScheme() == null)
              {
                returnUrl = protocol + "://" + (useDomainName == null ? getServletRequest().getServerName() : useDomainName);

                int port = getServletRequest().getServerPort();

                if (port != 80 && port != 443)
                  returnUrl += ":" + port;

                if (getServletRequest().getContextPath() != null && !uri.getPath().startsWith(getServletRequest().getContextPath()))
                  returnUrl += getServletRequest().getContextPath();

                if (getServletRequest().getPathInfo() != null && !uri.getPath().contains(getServletRequest().getPathInfo()))
                  returnUrl += getServletRequest().getServletPath();

                if (forwardUrl.charAt(0) != '/')
                  returnUrl += "/";
              }
          }
        catch (Exception e)
          {
            throw new WebAppException(e);
          }
        
        returnUrl += forwardUrl;

        if (rewrite)
          if (urlData.isRewrite())
            if (urlData.isRedirect())
              returnUrl = getServletResponse().encodeRedirectURL(returnUrl);
            else
              returnUrl = getServletResponse().encodeURL(returnUrl);

        return returnUrl;
      }

    /**
     * Returns an XML defined URL definition (url, forward, redirect) plus the query string.
     * 
     * @param key the key (id) as defined in the XML file
     * 
     * @return the defined URL plus the query string
     */
    public String getUrlWithQueryString(String key) { return getUrl(key, false, false) +  "?" + getServletRequest().getQueryString(); }
    
    /**
     * Returns an XML defined URL definition (url, forward, redirect) plus the query string.
     * Optionally includes all parts (protocol, host, etc).  Also optionally 
     * rewrites the URL for cookie limited browsers.
     * 
     * @param key the key (id) as defined in the XML file
     * @param fullUrl Optionally include all parts (protocol, host, etc.)
     * @param rewrite optionally rewrites the URL for cookie limited browsers
     * 
     * @return the defined URL plus the query string
     */
    public String getUrlWithQueryString(String key, boolean fullUrl, boolean rewrite) { return getUrl(key, fullUrl, rewrite) +  "?" + getServletRequest().getQueryString(); }
    
    /**
     * Returns an XML defined URL definition (url, forward, redirect).
     * 
     * @param key the key (id) as defined in the XML file
     * 
     * @return the defined URL
     */
    public String getUrl(String key) { return getUrl(key, false, false); }
      
    /**
     * Returns an XML defined URL definition (url, forward, redirect).
     * Optionally includes all parts (protocol, host, etc).  Also optionally 
     * rewrites the URL for cookie limited browsers.
     * 
     * @param key the key (id) as defined in the XML file
     * @param fullUrl Optionally include all parts (protocol, host, etc.)
     * @param rewrite optionally rewrites the URL for cookie limited browsers
     * 
     * @return the defined URL
     */
    public String getUrl(String key, boolean fullUrl, boolean rewrite) { return (String)urlDataWrapper.get(key, fullUrl, rewrite); }

    /**
     * Returns an XML defined message.
     * 
     * @param key the key (id) as defined in the XML file
     * 
     * @return the message value as defined in the XML
     */
    public String getMessage(String key) { return dataManager.getMessage(key); }
    
    UrlDataWrapper getUrlDataWrapper() { return urlDataWrapper; }
    
    class UrlDataWrapper implements Map
      {
        public void clear() { }
        public boolean containsKey(Object obj) { return dataManager.getUrlDataMap().containsKey(obj); }
        public boolean containsValue(Object obj) { return dataManager.getUrlDataMap().containsValue(obj); }
        public java.util.Set entrySet() { return dataManager.getUrlDataMap().entrySet(); }
        public boolean isEmpty() { return dataManager.getUrlDataMap().isEmpty(); }
        public Set keySet() { return dataManager.getUrlDataMap().keySet(); }
        public Object put(Object obj, Object obj1) { return null; }
        public void putAll(Map map) {  }
        public Object remove(Object obj) { return null; }
        public int size() { return dataManager.getUrlDataMap().size(); }
        public java.util.Collection values() { return dataManager.getUrlDataMap().values(); }
        
        public Object get(Object obj) { return get(obj, true, urlRewriting); }
        
        public Object get(Object obj, boolean fullUrl, boolean rewrite)
          {
            UrlData urlData = dataManager.getUrlData((String)obj);
            
            if (urlData == null)
              urlData = new UrlData("UrlDataWrapper", (String)obj);

            try
              {
                return getBuiltUrl(urlData, fullUrl, rewrite);
              }
            catch (Exception e)
              {
                throw new RuntimeException(e);
              }
          }
      }

    /**
     * Returns an instance of MultipartFormData, which contains all the information 
     * and abilities needed for dealing with multipart form data.
     * @param parameter the parameter of interest
     * @return an instance of MultipartFormData
     */
    public MultipartFormData getMultipartFormData(String parameter)
      {
        if (multipartFormDataMap == null)
          loadMultipartFormData();
        
        return (MultipartFormData)multipartFormDataMap.get(parameter);
      }
    
    //add by 
  	public Object getMultipartFormData2(String parameter) {
  		if (multipartFormDataMap == null)
  			loadMultipartFormData();

  		return multipartFormDataMap.get(parameter);
  	}

    
    /**
     * Returns returns the string data associated with the form data defined by parameter.
     * @param parameter the parameter of interest
     * @return a string containing the form data
     */
    public String getMultipartFormParameter(String parameter)
      {
        MultipartFormData formData = getMultipartFormData(parameter);
        
        if (formData != null )
          if (formData.isFormField())
        	//------------------modify by ----------
        	try {
  				return formData.getString(getServletRequest().getCharacterEncoding());
  			} catch (WebAppException e) {
  				return "";
  			}
        	//----------------------------------------------
            //return formData.getString();//replace by code line:1219-1223
          else if (formData.getName() != null)
            return formData.getName();
        
        return null;
      }
    
    /**
     * Returns true if the request header indicates the request body is multi-part.
     * 
     * @return true if the request header indicates the request body is multi-part.
     */
    public boolean isMultipartFormData()
      {
        String contentType = getServletRequest().getContentType();
        
        return multipartFormDataMap != null || (contentType != null && contentType.toLowerCase().startsWith("multipart"));
      }
    
    /**
     * Loads the multipart form data. Uses the defaults of 102400 for file 
     * threshold and "/WEB-INF/secure_uploads" for the file directory.
     */
    public void loadMultipartFormData()
      {
    	if (multipartFormDataMap == null)//add by 
        loadMultipartFormData(0, new File(getServletContext().getRealPath("/WEB-INF/secure_uploads")));
      }
    
    /**
     * Loads the multipart form data and allows definition of file size threshold 
     * and temp file repository for uploads.  The file size threshold defines whether uploaded files will 
     * be stored in memory (equal or less than threshold) or on disk (greater than threshold).
     * @param sizeThreshold the size value at which files will be written to disk
     * @param tempRepository the file directory to write the files to
     */
    public void loadMultipartFormData(int sizeThreshold, File tempRepository)
      {
        if (! tempRepository.exists())
          tempRepository.mkdirs();

        try
          {
            ServletFileUpload fileUpload = new ServletFileUpload(new DiskFileItemFactory(sizeThreshold, tempRepository));
            List fileList = fileUpload.parseRequest(getServletRequest());

            Iterator i = fileList.iterator();

            multipartFormDataMap = new HashMap();

            while (i.hasNext())
              {
                MultipartFormData formData = new MultipartFormData((DiskFileItem)i.next());

                //multipartFormDataMap.put(formData.getFieldName(), formData);
				//-------------------------mod by -----------------------------
				String key = formData.getFieldName();
				Object obj = null;
				if ((obj = multipartFormDataMap.get(key)) == null) {
					multipartFormDataMap.put(key, formData);
				} else {
					if (obj instanceof MultipartFormData) {
						List<MultipartFormData> list = new ArrayList<MultipartFormData>();
						list.add((MultipartFormData) obj);
						list.add(formData);
						multipartFormDataMap.put(key, list);
						list = null;
					} else {
						List<MultipartFormData> list = (List<MultipartFormData>) obj;
						list.add(formData);
						multipartFormDataMap.put(key, list);
						list = null;
					}
				}
				//-------------------------end-----------------------------
              }
          }
        catch (Exception e)
          {
            log("Could not parse multi part form data");
            log(e.toString(), e);
          }
      }
    
    /**
     * Class representing a multipart form element and methods to manipulate 
     * and retreive form element data.
     */
    public static class MultipartFormData
      {
        DiskFileItem fileItem;

        MultipartFormData(DiskFileItem fileItem)
          {
            this.fileItem = fileItem;
          }

        /**
         * Returns the character set definition.
         * @return a String 
         */
        public String getCharSet() { return fileItem.getCharSet(); }

        /**
         * delete the multipart uploaded file from memory or disk.
         */
        public void delete() { fileItem.delete(); }

        /**
         * Returns the content type.
         * @return a String 
         */
        public String getContentType() { return fileItem.getContentType(); }

        /**
         * Returns the field name as defined in the HTML form element.
         * @return a String 
         */
        public String getFieldName() { return fileItem.getFieldName(); }

        /**
         * Returns the multipart form elements value in bytes.  Suitable for small file uploads.
         * @return an array of bytes
         */
        public byte[] getBytes() { return fileItem.get(); }

        /**
         * The name of the file selected by the user for upload (see HTML &lt;input type="file" ...&gt;).
         * @return a String 
         */
        public String getName() { return fileItem.getName(); }

        /**
         * Returns the size of the data value.
         * @return a long
         */
        public long getSize() { return fileItem.getSize(); }

        /**
         * Returns an InputStream that can be used to read the uploaded file.
         * @return an instance of InputStream
         */
        public InputStream getInputStream() throws WebAppException
          {
            try
              {
                return fileItem.getInputStream(); 
              }
            catch (Exception e)
              {
                throw new WebAppException(e);
              }
          }

        //public OutputStream getOutputStream()  { return fileItem.getOutputStream(); }

        /**
         * Returns the data value as a string.
         * @return a String
         */
        public String getString() { return fileItem.getString(); }

        /**
         * Returns the data value as a string.
         * @param encodingType the encoding type for the string
         * @return a String
         */
        public String getString(String encodingType) throws WebAppException
          {
            try
              {
                return fileItem.getString(encodingType); 
              }
            catch (Exception e)
              {
                throw new WebAppException(e);
              }
          }

        /**
         * Returns true if the multipart form data element is a simple form value.
         * @return true if the multipart form data element is a simple form value, false otherwise
         */
        public boolean isFormField() { return fileItem.isFormField(); }

        /**
         * Returns true if the uploaded file is stored in memory.
         * @return true if the uploaded file is stored in memory, false if stored on disk
         */
        public boolean isInMemory() { return fileItem.isInMemory(); }

        /**
         * Returns the uploaded file's stored location in the local disk.
         * @return the uploaded file's stored location in the local disk
         */
        public File getStoredLocation() { return fileItem.getStoreLocation(); }

        /**
         * Write the uploaded file to a new location.
         * @param file the new files location
         */
        public void write(String file) throws WebAppException
          {
            try
              {
                write(new File(file)); 
              }
            catch (Exception e)
              {
                throw new WebAppException(e);
              }
          }

        /**
         * Write the uploaded file to a new location.
         * @param file the new files location
         */
        public void write(File file) throws WebAppException
          {
            try
              {
                fileItem.write(file); 
              }
            catch (Exception e)
              {
                throw new WebAppException(e);
              }
          }
      }
  }


